<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="type" content="guides2"/>
<meta name="account" content="false"/><meta name="viewport" content="width=device-width"/>
<title>CNBlockly - 进阶教程 - 2 - 可选输入与可选输出</title>
<link rel="stylesheet" href="../css/mdui.min.css">
<link rel="stylesheet" href="../css/style.css">
<script src="../js/mdui.min.js"></script><script src="../js/account.js"></script>

</head>
<body class="mdui-bottom-nav-fixed">
<div class="mdui-toolbar mdui-color-indigo">
  <a href="../guides.html" class="mdui-btn mdui-btn-icon"><i class="mdui-icon material-icons">arrow_back</i></a>
  <span class="mdui-typo-title">积木编辑器高级教程 - 可选输入与可选输出</span>
</div>
<div class="mdui-card">
  <div class="mdui-card-header">
	<img class="mdui-card-header-avatar" scr="https://ws1.sinaimg.cn/large/ebc3e24aly1fk46infk3gj208c08ct8z.jpg">
    <div class="mdui-card-header-title">积木编辑器高级教程</div>
    <div class="mdui-card-header-subtitle">by Bloody_Phono</div>
  </div>
      <div class="mdui-card-primary">
        <div class="mdui-card-primary-title">可选输入与可选输出</div>
		<div class="mdui-card-content mdui-typo">
		<p><em>建议大家在阅读这篇教程时，打开自己的编辑器，跟着教程中所说的步骤演练一番，印象能更深刻一些</em><br />前置教程：<a href="advanced-1.html">面向对象初步</a></p><hr /><h1>一、XML</h1><p>先不要被这种陌生的英文缩写词吓到，对于我们的积木来说，XML就是对积木形状以及位置等信息的描述而已。<br />跟我来：新建一个空白作品，把下面这个积木拖出来，调成图里的样子。<br /><img src="http://bbs.pvpin.com/uploads/files/1503730978212-image.png" alt="0_1503730966794_image.png" class="img-responsive img-markdown" /><br />然后找到上方<strong>JavaScript</strong>右边的<strong>xml</strong>按钮，点一下，你应该能看到这样一些东西：<br /><img src="http://bbs.pvpin.com/uploads/files/1503731362297-image.png" alt="0_1503731350866_image.png" class="img-responsive img-markdown" /><br />第一行与最后一行固定的首尾，不用管它。第二行的<code>&lt;block ...&gt;</code>与倒数第二行的<code>&lt;/block&gt;</code>表示，这两个标签以及它们中间的内容，是一个block，也就是你刚才拖出来的这个。<br />第二行，<code>&lt;block ...&gt;</code>标签后半部分内容很好理解：积木名字与横纵坐标。<br />第三行一会儿再说。<br />第四、五行的内容，相信你看一眼就知道，分别代表的是两处控件（也就是那两个下拉列表）。如这段xml代码所示，我们第一个下拉列表的值为<code>GET</code>（即<code>获得项</code>），第二个下拉列表的值为<code>FROM_START</code>（即<code>位置</code>）。<br />你可以多找几个积木来，看看它们的xml代码分别是什么样。</p><hr /><h1>二、mutation</h1><p><em>(这单词我没找着一个合适的意思就先不翻译了……)</em><br />让我们回到第三行的<code>&lt;mutation...&gt;</code>标签。它有两个属性：<code>statement</code>和<code>at</code>。一个是<code>&quot;false&quot;</code>，一个是<code>&quot;true&quot;</code>。注意引号，引号代表它们的值是字符串而非布尔值。<br />那这两个属性是干什么的？<br />我们试试这个积木两个下拉列表的其他选项，然后看看它们的xml，我们会发现：<br />当<code>MODE</code>列表（第一个下拉列表）的选项为<code>移除项</code>时，<code>statement</code>属性的值会变为<code>true</code>；其他两个选项下，<code>statement</code>属性的值则是<code>false</code>；而对于积木形状来说，当选项为<code>移除项</code>时，它是上下连接的；当选项为其他两个选项时，它是左连接的。<br /><img src="http://bbs.pvpin.com/uploads/files/1503732399804-image.png" alt="0_1503732388362_image.png" class="img-responsive img-markdown" /><br />换句话说，当选项为<code>移除项</code>时，<code>statement</code>的值为<code>&quot;true&quot;</code>，积木为上下连接；否则，值为<code>&quot;false&quot;</code>，积木为左连接。<br />对于<code>WHERE</code>列表来说也是类似的。不难发现当选项为前两项时，<code>at</code>值为<code>&quot;true&quot;</code>，积木给了一个输入的插口；其他情况下，<code>at</code>值为<code>&quot;false&quot;</code>，没有插口。<br />事实上，当发现应该进行形变时，积木将关键信息记录到xml文件中的<code>mutation</code>标签下，之后再根据标签中的属性，更新自己的形状。<br />这样做的好处是，既不耽误正常形变，又不会导致刷新一下后又会变回原来的样子。</p><hr /><h1>三、动手写一个可选输出的积木</h1><p>让我们来写这么一个积木（默认是第一种形态）<br /><img src="http://bbs.pvpin.com/uploads/files/1503742386220-image.png" alt="0_1503742374827_image.png" class="img-responsive img-markdown" /><br />既然默认是第一种形态，那我们先写个第一种形态那样的普通积木：<br /><img src="http://bbs.pvpin.com/uploads/files/1503742850144-image.png" alt="0_1503742838654_image.png" class="img-responsive img-markdown" /><br />接下来，让我们把其他几个函数的框架补上：<br /><img src="http://bbs.pvpin.com/uploads/files/1503743104667-image.png" alt="0_1503743092980_image.png" class="img-responsive img-markdown" /><br />其中<code>mutationToDom</code>和<code>domToMutation</code>的名字不可以变，<code>domToMutation</code>函数也必须带一个参数（但参数名字可以变）。<br />先不考虑<code>mutation</code>，我们先把<code>updateShape</code>完成了。<br />完成<code>updateShape</code>函数之前，我们先提升一下自己的知识水平：先把代码改成下图这样：<br /><img src="http://bbs.pvpin.com/uploads/files/1503743451337-image.png" alt="0_1503743439734_image.png" class="img-responsive img-markdown" /><br />注意第11行的最右边的第二个参数别忘了加上。<br />如注释所说，第二个参数指定了当用户选择选项后要调用的函数。换句话说，当我们选择了下拉列表中的<code>无输出</code>这一选项后，就会执行<code>updateShape(false)</code>。</p><blockquote><p><code>Q:BPBP，为什么第六行那里的this后面有个sourceBlock_呀？为什么其他this的后面没有？</code><br /><code>A:因为在12、13、14行的这行this指代是这个block本体。至于第六行的this，由于它所在的func函数被我们作为参数传给了第11行，所以这个this指的是field_dropdown这个控件。为了让block而非这个控件更新形状，我们需要用this.sourceBlock_获取它所在的block，然后调用这个block的updateShape函数</code></p></blockquote><p>这样，当用户选择<code>有输出</code>选项后，系统就会对这个<code>block</code>执行<code>updateShape(true)</code>，反之同理。<br />好的，知识水平提升完毕，我们现在来完成<code>updateShape</code>函数。<br />先给你们看看这个函数最后写好是什么样：<br /><em>(如果发现有的代码的行数和之前不一样，不要慌，因为我删掉了之前的注释，不一样是正常的)</em><br /><img src="http://bbs.pvpin.com/uploads/files/1503744214766-image.png" alt="0_1503744203201_image.png" class="img-responsive img-markdown" /><br />我们的逻辑是，如果用户选择前后的选项是一样的（比如本来就是<code>有输出</code>，然后用户又选择了<code>有输出</code>），那么这种情况下是不需要形变的。<br />所以在第13行我们先看看积木此时有没有左连接的输出接头。<code>this.outputConnection</code>是这个接头本体，连用两个感叹号等同于"有没有这个接头”，也就相当于&quot;之前的选项了&quot;<br />在第15行，我们判断这两个布尔值是否相同。如果相同，我们就不进行变形操作。<br />如果不同，那我们开始变形：<br />在第16行，我们调用积木的<code>unplug</code>成员函数，让它和其他积木断开连接。<strong>这一步是必要的，否则会出错，严重的话会让包含这个积木的整个作品都打不开，直接作废。</strong><br />在第18到26行，我们根据新值来更新我们的形状。<code>setPreviousStatement</code>和<code>setNextStatement</code>是上下连接，<code>setOutput</code>是左连接，相信需要这篇教程的你肯定对此不陌生。<br />到这里，你的积木其实单说变形的话，已经可以变形了！快去试试吧！等你兴奋一下，然后我们再继续往下看。</p><p>那么我们还有什么工作要做？<br />你的积木现在是可以变形了，但是，它的变形是临时性的。<br />什么意思呢？就是你先点开一下xml界面，再回到你的积木界面（这样相当于一次刷新）。你会发现你的积木的形状又变回去了。<br />这是为什么呢？因为你没有把它的形状的改动保留到xml代码中。你可以把xml代码理解成蓝图。每次刷新，积木们相当于是按照蓝图重新生成了一遍。蓝图里默认记载了积木的种类、坐标、选项的值等等内容（也就是这篇教程最开始那幅xml代码图的二、四、五行），没有记载形状这个信息。我们现在要做的，就是把形状信息记录到"蓝图”中去：<br /><img src="http://bbs.pvpin.com/uploads/files/1503745274345-image.png" alt="0_1503745262896_image.png" class="img-responsive img-markdown" /><br />第34行的<code>container</code>就是xml代码中的那个<code>&lt;mutation ... &gt;</code>标签；<br />第35行，获取形状信息；第36行，记录形状信息；<br />第37行，<code>mutationToDom</code>这个函数必须返回<code>&lt;mutation ...&gt;</code>这个标签。<br />第41行，从蓝图文件中获取形状信息。注意最右侧，比较的对象是<code>'true'</code>而非<code>true</code>；<br />第42行，将比较的结果作为参数<code>hasOutput</code>，调用<code>updateShape</code>函数。</p><blockquote><p><code>Q:BPBP，你不是说属性的值是字符串而不是布尔值吗？那为什么36行那里直接用hasOutput这个布尔值来设置output这个属性的值了呀？不需要先转换一下的吗？</code><br /><code>A:不需要。</code><br /><code>Q: 哦。。。</code></p></blockquote><p>到这里，才算是完成了！这下你的积木可以随心所欲的变形了！站着变坐着变躺着变，怎么变都不用怕，再也不用担心侧……不好意思走错片场了。<br />完整的代码如下（保留了一些必要的注释）：<br /><img src="http://bbs.pvpin.com/uploads/files/1503745975814-image.png" alt="0_1503745964247_image.png" class="img-responsive img-markdown" /></p><hr /><h1>四、可选输入的积木</h1><p><img src="http://bbs.pvpin.com/uploads/files/1503831506563-image.png" alt="0_1503831494177_image.png" class="img-responsive img-markdown" /><br /><img src="http://bbs.pvpin.com/uploads/files/1503831554955-image.png" alt="0_1503831542472_image.png" class="img-responsive img-markdown" /></p>
</div>
		<div class="mdui-card-actions">
        <a class="mdui-btn mdui-ripple mdui-ripple-white" href="../guides.html">返回</a>
		<a class="mdui-btn mdui-ripple mdui-ripple-white" href="advanced-3.html">下一章</a>
		</div>
  </div>
</div>
</br>
<iframe name="content_frame" marginwidth=0 marginheight=0 width=100% height=100 src="../copyright.html" frameborder=0></iframe>
<div id="nav"></div>
</body>
</html>